package com.lyp.jackson.server.service.impl;

import com.lyp.jackson.api.common.annotation.ServiceLimit;
import com.lyp.jackson.api.common.annotation.Servicelock;
import com.lyp.jackson.api.common.enums.SeckillStatEnum;
import com.lyp.jackson.model.entity.Seckill;
import com.lyp.jackson.model.entity.SuccessKilled;
import com.lyp.jackson.model.mapper.SeckillMapper;
import com.lyp.jackson.model.mapper.SuccessKilledMapper;
import com.lyp.jackson.server.response.Result;
import com.lyp.jackson.server.service.SeckillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class SeckillServiceImpl implements SeckillService {
    /**
     * 思考：为什么不用synchronized
     * service 默认是单例的，并发下lock只有一个实例
     */
    private Lock lock = new ReentrantLock(true);//互斥锁 参数默认false，不公平锁

    @Autowired
    private SeckillMapper seckillMapper;

    @Autowired
    private SuccessKilledMapper successKilledMapper;

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Override
    public Seckill selectBySeckillId(long seckillId) {
        return seckillMapper.selectBySeckillId(seckillId);
    }

    @Override
    @Transactional
    public void deleteSeckill(long seckillId) {
        //TODO：还原秒杀库存表
        seckillMapper.recoverySeckill(seckillId);
        //TODO：清空秒杀明细表
        successKilledMapper.emptySuccessKilled(seckillId);
    }


    /**
     * 普通秒杀，最low实现
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public Result startSeckil(long seckillId, long userId) {
        //TODO:校验库存
        Seckill seckill = seckillMapper.selectBySeckillId(seckillId);
        if (seckill != null && seckill.getStockNumber() > 0) {
            //TODO:扣除库存
            seckillMapper.updateSeckillBySeckillId(seckillId);
            //TODO:创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState((short) 0);
            successKilled.setCreateTime(new Date());
            successKilledMapper.saveSuccessKilled(successKilled);
            //TODO:支付逻辑

            return Result.ok(SeckillStatEnum.SUCCESS);
        } else {
            //TODO：秒杀失败
            return Result.error(SeckillStatEnum.END);
        }
    }

    /**
     * 普通秒杀(AOP限流处理)
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
    @ServiceLimit
    @Transactional
    public Result startSeckilLimit(long seckillId, long userId) {
        //TODO:校验库存
        Seckill seckill = seckillMapper.selectBySeckillId(seckillId);
        if (seckill != null && seckill.getStockNumber() > 0) {
            //TODO:扣除库存
            seckillMapper.updateSeckillBySeckillId(seckillId);
            //TODO:创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState((short) 0);
            successKilled.setCreateTime(new Date());
            successKilledMapper.saveSuccessKilled(successKilled);
            //TODO:支付逻辑

            return Result.ok(SeckillStatEnum.SUCCESS);
        } else {
            //TODO：秒杀失败
            return Result.error(SeckillStatEnum.END);
        }
    }

    /**
     * 秒杀程序锁Lok(会出现超卖101的现象):
     * 原因：lock锁是在事物单元中执行的，秒杀开始后，某个事物在未提交之前，
     * 锁已经释放(事物提交是在整个方法执行完)，导致下一个事物读取到了上个事物未提交的数据，也就是传说中的脏读。
     * 解决办法：建议是锁上移,上移到Controller层，也就是说要包住整个事物单元，另外解决办法就是使用AOP切面编程解决，下面介绍。
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public Result startSeckilLock(long seckillId, long userId) {
        try {
            lock.lock();//上锁

//            //TODO:校验库存
//            Seckill seckill = seckillMapper.selectBySeckillId(seckillId);
//            if (seckill.getStockNumber() > 0) {
//                //TODO:扣除库存
//                seckillMapper.updateSeckillBySeckillId(seckillId);
//                //TODO:创建订单
//                SuccessKilled successKilled = new SuccessKilled();
//                successKilled.setSeckillId(seckillId);
//                successKilled.setUserId(userId);
//                successKilled.setState(Short.parseShort(seckill.getStockNumber()+""));
//                successKilled.setCreateTime(new Date());
//                successKilledMapper.saveSuccessKilled(successKilled);
//                //TODO:支付逻辑
//            } else {
//                //TODO：秒杀失败
//                return Result.error(SeckillStatEnum.END);
//            }

            String sql="SELECT stock_number FROM seckill WHERE seckill_id=?";
            Object args[] = {seckillId};
            Map<String, Object> stringObjectMap = jdbcTemplate.queryForMap(sql, args);
            if (stringObjectMap.size()>0 && Long.valueOf(stringObjectMap.get("stock_number").toString())>0){
                String sql2="UPDATE seckill  SET stock_number=stock_number-1 WHERE seckill_id=?";
                Object args2[] = {seckillId};
                jdbcTemplate.update(sql2,args2);
                String sql3="insert into success_killed (seckill_id,user_id,state,create_time) value (?,?,?,?)";
                Object args3[] = {seckillId,userId,Short.parseShort(stringObjectMap.get("stock_number")+""),new Timestamp(new Date().getTime())};
                jdbcTemplate.update(sql3,args3);

            }else {
                return Result.error(SeckillStatEnum.END);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//释放锁
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }

    /**
     * AOP+锁实现
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
    @Servicelock
    @Transactional
    public Result startSeckilAopLock(long seckillId, long userId) {
        //TODO:校验库存
        Seckill seckill = seckillMapper.selectBySeckillId(seckillId);
        if (seckill != null && seckill.getStockNumber() > 0) {
            //TODO:扣除库存
            seckillMapper.updateSeckillBySeckillId(seckillId);
            //TODO:创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState((short) 0);
            successKilled.setCreateTime(new Date());
            successKilledMapper.saveSuccessKilled(successKilled);
            //TODO:支付逻辑

            return Result.ok(SeckillStatEnum.SUCCESS);
        } else {
            //TODO：秒杀失败
            return Result.error(SeckillStatEnum.END);
        }
    }


    /**
     * 秒杀数据库悲观锁一 FOR UPDATE
     * 注意这里 限流注解 可能会出现少买 自行调整
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
//    @ServiceLimit
    @Transactional
    public Result startSeckilDBPCC_ONE(long seckillId, long userId) {
        //TODO:校验库存
        Seckill seckill = seckillMapper.selectSeckilDBPCC_ONE(seckillId);
        if (seckill != null && seckill.getStockNumber() > 0) {
            //TODO:扣除库存
            seckillMapper.updateSeckillBySeckillId(seckillId);
            //TODO:创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState((short) 0);
            successKilled.setCreateTime(new Date());
            successKilledMapper.saveSuccessKilled(successKilled);
            //TODO:支付逻辑

            return Result.ok(SeckillStatEnum.SUCCESS);
        } else {
            //TODO：秒杀失败
            return Result.error(SeckillStatEnum.END);
        }
    }


    /**
     * 秒杀数据库悲观锁二 stock_number>0
     *
     * @param seckillId
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public Result startSeckilDBPCC_TWO(long seckillId, long userId) {
        //TODO:扣除库存 UPDATE锁表
        int count = seckillMapper.updateSeckilDBPCC_TWO(seckillId);
        if (count > 0) {
            //TODO:创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState((short) 0);
            successKilled.setCreateTime(new Date());
            successKilledMapper.saveSuccessKilled(successKilled);
            //TODO:支付逻辑

            return Result.ok(SeckillStatEnum.SUCCESS);
        } else {
            //TODO：秒杀失败
            return Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    public Result startSeckilDBOCC(long seckillId, long userId, long number) {
        return null;
    }
}
